package com.atguigu.gmall.index.aspect;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.index.annotation.GmallCache;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class GmallCacheAspect {

    @Autowired
    private RBloomFilter bloomFilter;

    @Autowired
    RedissonClient redisClient;

    @Autowired
    StringRedisTemplate redisTemplate;





    @Around("@annotation(com.atguigu.gmall.index.annotation.GmallCache)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取方法名
        Method method = signature.getMethod();
        //获取对象上的指定注解
        GmallCache gmallCache = method.getAnnotation(GmallCache.class);
        //获取注解中的前缀
        String prefix = gmallCache.prefix();

        //方法的形参列表，这里用逗号分割，组装成字符串
        String ages = StringUtils.join(joinPoint.getArgs(), ",");
        String key = prefix + ages;

        //为了防止缓存穿透使用了布隆过滤器
        if (!bloomFilter.contains(key)) {
            return null;
        }
       //先查询缓存，如果命中直接返回
        String json = this.redisTemplate.opsForValue().get(key);
        if (StringUtils.isNotEmpty(json)){
            return JSON.parseObject(json,signature.getReturnType());
        }

        //防止缓存击穿添加分布式锁
        String lock_prefix = gmallCache.lock();
        RLock lock = this.redisClient.getLock(lock_prefix+ages);
        lock.lock();

        try {
            //在查询缓存，命中直接返回
            String json2 = this.redisTemplate.opsForValue().get(prefix + ages);
            if (StringUtils.isNotBlank(json2)){
                return JSON.parseObject(json2,signature.getReturnType());
            }

            //执行目标方法
            Object result = joinPoint.proceed(joinPoint.getArgs());

            //放入缓存，并释放分布式锁
            int timeout = gmallCache.timeout() + new Random().nextInt(gmallCache.random());
            this.redisTemplate.opsForValue().set(key,JSON.toJSONString(result), timeout, TimeUnit.MINUTES);
            return result;
        } finally {
            lock.unlock();
        }
    }
}
